/// File: VSLabFSICore\viewlet.fs
/// 
/// Author: Antonio Cisternino (cisterni@di.unipi.it)
/// Author: Davide Morelli (morellid@di.unipi.it)
/// Author: Giacomo Righetti (righetti@di.unipi.it)
///   
/// ------------------------------------------------------------
/// Copyright (c) Microsoft Corporation.  All rights reserved.
/// 
/// The use and distribution terms for this software are 
/// contained in the file named license.txt, which can be found 
/// in the root of this distribution.
/// By using this software in any fashion, you are agreeing to 
/// be bound by the terms of this license.
///
/// You must not remove this notice, or any other, from this
/// software.
/// ------------------------------------------------------------

namespace VSLab

open System
open System.Windows.Forms
open System.Drawing
open System.Drawing.Drawing2D
open System.Runtime.InteropServices
open System.Net
open System.Net.Sockets
open System.Runtime.Serialization.Formatters.Binary
open System.Threading

// this is neede only to be able to tell VS where the assembly is
open VSLabCore
open LightweightControls
open VSLab.Environment

/// <summary>
/// The enumeration containing the WM_ messages used to proxy the Viewlets.
/// </summary>
type Messages =
  | WM_MOUSEMOVE = 0x0200
  | WM_LBUTTONDOWN = 0x0201
  | WM_LBUTTONUP = 0x0202
  | WM_LBUTTONDBLCLK = 0x0203
  | WM_RBUTTONDOWN = 0x0204
  | WM_RBUTTONUP = 0x0205
  | WM_RBUTTONDBLCLK = 0x0206
  | WM_MBUTTONDOWN = 0x0207
  | WM_MBUTTONUP = 0x0208
  | WM_MBUTTONDBLCLK = 0x0209
  | WM_MOUSEWHEEL = 0x020A
  | WM_MOUSELEAVE = 0x02A3
  | WM_PAINT = 0x000F
  | WM_ERASEBKGND = 0x0014
  | WM_APP = 0x8000
  | WM_APP_PAINT = 0x8001
  | WM_APP_ERASEBKGND = 0x8002
  | WM_APP_VISIBLECHANGED = 0x8003
  | WM_APP_SHOWCARET = 0x8004
  | WM_APP_CARETPOS = 0x8005
  | WM_APP_CARETSZ = 0x8006
  | WM_APP_SETCURSOR = 0x8007
  | WM_APP_INVALIDATE = 0x8008
  | WM_APP_VISIBLECHANGEDREQ = 0x8009
  | WM_SIZE = 0x5
  | WM_KEYDOWN = 0x0100
  | WM_KEYUP = 0x0101

/// <summary>
/// This class implements a tcp server
/// this has to be in a separate thread
/// to avoid deadlocks
/// </summary>
type internal SocketServer() as x =
  let mutable hwndSrc = IntPtr.Zero
  let mutable hwndDest = IntPtr.Zero
  /// <summary>
  /// The handle of the Viewlet's User Control (in the F# Interactive Process)
  /// </summary>
  member x.HwndSrc
    with get() = hwndSrc
    and set(v) = hwndSrc <- v
  /// <summary>
  /// The handle of the Visual Studio Tool Window (in the VS Process)
  /// </summary>
  member x.HwndDest
    with get() = hwndDest
    and set(v) = hwndDest <- v
  /// <summary>
  /// Creates the Socket Server. This method is only called when the Viewlet is shown for the first time.
  /// </summary>
  member x.CreateServer() =
    // create a socket
    let ipAddr = IPAddress.Parse("127.0.0.1")
    let tcpListener = new TcpListener(ipAddr, 13000)
    let bytes = (Array.create 4 0uy : byte[])
    let hwndSrcBytes = (Array.create 4 0uy : byte[])
    tcpListener.Start()
    let tcpClient = tcpListener.AcceptTcpClient()
    let stream = tcpClient.GetStream()
    let formatter = new BinaryFormatter()
    // send HWND
    formatter.Serialize(stream, hwndSrc)
    // send String (id and type)
    // TODO
    // read HWND
    hwndDest <- unbox<IntPtr> (formatter.Deserialize(stream))
    // read GUID
    // TODO
    tcpClient.Close()
    tcpListener.Stop()    

open EnvDTE
open EnvDTE80

/// <summary>
/// This type has been introduced to keep track of the state
/// of a click to emulate Click and DoubleClick events since
/// WinForms finds that the handle of the window is different
/// than the one generating the click or dblclick event.
/// Have a Reflector look to Control::WndProc, Control::WmMouseDown
/// and Control::WmMouseUp.
/// We preserved the semantics of WinForms here that assumes that
/// button-down+button-up = click whatever button has been pressed.
/// </summary>
type internal ClickState =
| None
| Down
| DoubleClick
    
/// <summary>
/// The class that must be inerited to implement a viewlet.
/// Takes care of proxying WM_ messages to and from Visual Studio.
/// </summary>
type Viewlet() as x =
  inherit LightweightContainer()
  
  let mutable name = "VSLab Viewlet"
  let mutable target = IntPtr.Zero
  let mutable toolwnd : Window2 = null
  let mutable width = 0
  let mutable height = 0
  let mutable caretVisible = false
  let mutable caretSize = new Size()
  let mutable caretPosition = new Point()
  // by default let's create a new guid
  let mutable guid = System.Guid.NewGuid().ToString("B")
  
  let mutable clickstate = ClickState.None
  let mutable mousemoveleave = false
  let mutable mousein = false
   
  let mutable suppressInvalidate = false
  let mutable suppressOnInvalidated = false
     
  let mutable updating = false
  
  let mutable doubleBuffer = null
    
  // This is WinForms implementation dependent
  // The event is skipped.
  let WM_MOUSEENTER = RegisterWindowMessage("WinFormsMouseEnter")
        
  do
    _viewletsProxy.Controls.Add(x)

  /// <summary>
  /// This allows for a global transformation of graphics context (i.e. transformation matrix)
  /// affecting control and children lightweight controls
  /// </summary>
  abstract member OnConfigureGraphics : g:Graphics -> unit
  default x.OnConfigureGraphics g = ()
  
  member private x.internalPaint (pe:PaintEventArgs) =
    if caretVisible then
      SendMessage(target, (int Messages.WM_APP_SHOWCARET), new IntPtr(3), IntPtr.Zero) |> ignore
    // save the graphics state, delegates the painting to the concrete viewlet .. 
    x.OnConfigureGraphics(pe.Graphics)
    let gs = pe.Graphics.Save()
    x.OnPaint(pe)
    pe.Graphics.Restore(gs)
    // .. then paint lightweight controls.
    x.PaintChildrenControls(pe)
    if caretVisible then
      SendMessage(target, (int Messages.WM_APP_SHOWCARET), new IntPtr(2), IntPtr.Zero) |> ignore

  member x.SetViewletStyle (s, v) =
    // Fixme: find how to call SetStyle...
    if int(s &&& ControlStyles.DoubleBuffer) <> 0 || int(s &&& ControlStyles.OptimizedDoubleBuffer) <> 0 then
      if v && doubleBuffer = null then
        doubleBuffer <- new Bitmap(x.Size.Width, x.Size.Height)
        x.Invalidate()
      else if not v && doubleBuffer <> null then
        doubleBuffer.Dispose()
        doubleBuffer <- null
  
  /// <summary>
  /// Returns the Viewlet's handle, used to proxy the Visual Studio Tool Window in F# Interactive
  /// </summary>
  member x.ViewletHandle 
    with get() = target
    
  /// <summary>
  /// The Viewlet's Width, Read Only
  /// </summary>
  member x.Width
    with get() = width

  /// <summary>
  /// The Viewlet's Height, Read Only
  /// </summary>
  member x.Height
    with get() = height

  /// <summary>
  /// The Viewlet's Unique Guid, used in Visual Studio to identify the tool window, save and load its position and docking state.
  /// </summary>
  member x.Guid 
    with get() = guid
    and set(v) = guid <- v

  /// <summary>
  /// The Viewlet's Name, it is also the Visual Studio Tool Window Caption Text.
  /// </summary>
  member x.Name 
    with get() = name
    and set(n) = name <- n

  /// <summary>
  /// The Visual Studio Tool Window, it is an instance of a DTE2.Window2.
  /// </summary>
  member x.ToolWindow
    with get() = toolwnd

  /// <summary>
  /// Set the size of the caret, it does not affect its visibility status.
  /// </summary>
  member x.CaretSize
    with get() = caretSize
    and set(v) =
      caretSize <- v
      SendMessage(target, (int Messages.WM_APP_CARETSZ), new IntPtr(v.Width), new IntPtr(v.Height)) |> ignore

  /// <summary>
  /// Set the position of the caret, it does not affect its visibility status.
  /// </summary>
  member x.CaretPosition
    with get() = caretPosition
    and set(v) =
      caretPosition <- v
      SendMessage(target, (int Messages.WM_APP_CARETPOS), new IntPtr(v.X), new IntPtr(v.Y)) |> ignore
  
  /// <summary>
  /// Set the visibility status of the caret of the Viewlet.
  /// </summary>
  member x.CaretVisible
    with get() = caretVisible
    and  set(v) = 
      caretVisible <- v
      SendMessage(target, (int Messages.WM_APP_SHOWCARET), new IntPtr(if v then 0 else 1), IntPtr.Zero) |> ignore

  /// <summary>
  /// Notifies the cursor to the true toolwindow
  /// </summary>
  override x.OnCursorChanged (e) =
      SendMessage(target, (int Messages.WM_APP_SETCURSOR), x.Cursor.Handle, IntPtr.Zero) |> ignore
      base.OnCursorChanged(e)
  
  /// <summary>
  /// Closes the Viewlet disposing all the used resources.
  /// </summary>
  member x.Close () =
    (x :> IDisposable).Dispose()
    target <- IntPtr.Zero
    toolwnd.Close(vsSaveChanges.vsSaveChangesNo)
    _viewletsProxy.Controls.Remove(x)
    _viewletsList.Remove(x) |> ignore
    populateHiddenFun()

  /// <summary>
  /// Event invoked when the Visual Studio Tool Window is destroyed, it closes the Viewle disposing the used resources.
  /// </summary>  
  /// <param name="e"> unused argument</param>
  override x.OnHandleDestroyed(e) =
    x.Close() |> ignore
    
  /// <summary>
  /// Hides the Viewlet
  /// </summary>
  abstract member Hide : unit -> unit
  /// <summary>
  /// Default implementation of the Hide abstract method
  /// </summary>
  default x.Hide() =
    if x.Visible then
      x.ToolWindow.Visible <- false
  
  /// <summary>
  /// Makes the Viewlet visible
  /// </summary>
  abstract member Show : unit -> unit
  /// <summary>
  /// Default implementation of the Show() abstract method.
  /// Viewlets overriding this method should call base.Show()
  /// </summary>
  default x.Show() =
    if target <> IntPtr.Zero && not x.Visible then
      x.ToolWindow.Visible <- true
    else
      // Create the toolwindow using DTE
      //let dte = System.Runtime.InteropServices.Marshal.GetActiveObject("VisualStudio.DTE") :?> EnvDTE80.DTE2
      let dte = FetchVSDTE _vspid
      // let's pick the first available..
      let mutable vslabAddin = dte.AddIns.Item(1)
      // now let's look for VSLab
      for i = 1 to dte.AddIns.Count do 
        if dte.AddIns.Item(i).Name = "VSLab" then
          vslabAddin <- dte.AddIns.Item(i)
      done
      // did we found the right addin?
      if vslabAddin.Name = "VSLab" then
        // we've found the VSLab addin, let's run the socket server
        let socketServer = new SocketServer()
        socketServer.HwndSrc <- x.Handle 
        let thread = new System.Threading.Thread(new ThreadStart(socketServer.CreateServer)) 
        thread.Start()
        // now to create the toolwindow...
        let programmableObject:obj = null
        let finestre = dte.MainWindow.Collection :?> Windows2
        // let's instantiate a toolWindow only to get its type name and assembly location
        let toolWnd = new ViewletToolWindow()      
        let toolWndName = toolWnd.GetType().FullName
        let toolWndAssemblyPath = toolWnd.GetType().Assembly.Location
        let toolWnd = finestre.CreateToolWindow2(vslabAddin, toolWndAssemblyPath, toolWndName, name, guid, ref programmableObject) 
        _viewletsList.Add(x :> UserControl)
        
        // searches for the control that has the focus                             
        let mutable focusedCtrl = -1, Int32.MaxValue
        for i = 0 to x.LightweightControls.Count - 1 do
          let ctrl = x.LightweightControls.[i]
          if ctrl.TabStop && ctrl.TabIndex < snd focusedCtrl then focusedCtrl <- i, ctrl.TabIndex
        if snd focusedCtrl <> Int32.MaxValue then x.SetFocus(fst focusedCtrl)
        
        // shows the viewlet
        toolWnd.Visible <- true
        toolwnd <- (toolWnd :?> Window2)

        let timer = new System.Windows.Forms.Timer()
        timer.Interval <- 2000
        timer.Tick.Add(fun _ ->
          SendMessage(target, int(Messages.WM_APP_VISIBLECHANGED), 0n, if x.ToolWindow.Visible then 1n else 0n) |> ignore
        )
        timer.Start()
              
        // at this point socketServer has served its role
        // because the comunication between SocketServer and ViewletToolWindow
        // is synchronous
        target <- socketServer.HwndDest
        
        // Update caret
        SendMessage(target, (int Messages.WM_APP_SHOWCARET), new IntPtr(if caretVisible then 0 else 1), IntPtr.Zero) |> ignore
        SendMessage(target, (int Messages.WM_APP_CARETSZ), new IntPtr(caretSize.Width), new IntPtr(caretSize.Height)) |> ignore
        SendMessage(target, (int Messages.WM_APP_CARETPOS), new IntPtr(caretPosition.X), new IntPtr(caretPosition.Y)) |> ignore
        
        populateHiddenFun()
        x.OnViewletVisibilityChange(true)
      else
        MessageBox.Show("Can't find the VSLab addin", "Error") |> ignore
      x.Invalidate()
     
  /// <summary>
  /// Paints the background of the Viewlet
  /// </summary>
  /// <param name="g"> the event argument containing the Graphic Context</param>
  override x.OnPaintBackground(g) =
    use b = new SolidBrush(base.BackColor)
    g.Graphics.FillRectangle(b, g.ClipRectangle)
      
  /// <summary>
  /// Invalidates a specific region of the Viewlet and causes a paint message to be sent to the control.
  /// </summary>
  /// <param name="r"> the Rectangle to be invalidated</param>
  member x.Invalidate(r:Rectangle) =
    if x.Visible && not updating then    
      suppressInvalidate <- true 
      
      let encode a b = (a <<< 16) ||| (b &&& 0xFFFF)
      let lt = encode r.Left r.Top
      let wh = encode r.Width r.Height
      SendMessage(x.Handle, int(Messages.WM_APP_INVALIDATE), new IntPtr(lt), new IntPtr(wh)) |> ignore

      // This was an error! timers tend to accumulate on pint...
      //if doubleBuffer = null then
      //  use g = Graphics.FromHwnd(target)
      //  let pe = new PaintEventArgs(g, r)
      //  g.SetClip(new RectangleF(float32 r.Left, float32 r.Top, float32 r.Width, float32 r.Height))
      //  x.OnPaintBackground(pe)
      //  x.internalPaint(pe)
      //else
      //  use g = Graphics.FromHwnd(target)
      //  use bg = Graphics.FromImage(doubleBuffer)
      //  let pe = new PaintEventArgs(bg, r)
      //  g.SetClip(new RectangleF(float32 r.Left, float32 r.Top, float32 r.Width, float32 r.Height))
      //  bg.SetClip(new RectangleF(float32 r.Left, float32 r.Top, float32 r.Width, float32 r.Height))
      //  x.OnPaintBackground(pe)
      //  x.internalPaint(pe)
      //  g.DrawImage(doubleBuffer, 0, 0)

      if not suppressOnInvalidated then
        x.OnInvalidated(new InvalidateEventArgs(r))
      suppressInvalidate <- false
    
  /// <summary>
  /// Invalidates the entire surface of the Viewlet and causes the control to be redrawn.
  /// </summary>
  member x.Invalidate() =
    x.Invalidate(new Rectangle(0, 0, width, height))
   
  /// <summary>
  /// Stops the viewlet from repainting.
  /// </summary>
  /// <remarks>
  /// Stops the viewlet from repainting every time a child control is added or is modified.
  /// Remember to call ResumeUpdating to enable again the repaint of the viewlet surface.
  /// </remarks>
  member x.SuspendUpdating() = updating <- true
  
  /// <summary>
  /// Resume the repainting of the viewlet surface.
  /// </summary>
  /// <param name="forceRepaing">set this param to true if an invalidate of the viewlet is needed.</param>
  member x.ResumeUpdating (forceRepaint) =
    updating <- false
    if forceRepaint then x.Invalidate()
      
  /// <summary>
  /// Resume the repainting of the viewlet surface.
  /// </summary>
  /// <remarks>
  /// This overload doesn't force the invalidation of the viewlet.
  /// <remarks>
  member x.ResumeUpdating() = 
    x.ResumeUpdating(false)
          
  /// <summary>
  /// If the event was raised by a call to Control.Invalidate forces a call to Viewlet.Invalidate 
  //  in order to make the Viewlet correctly redraw its children lightweight controls.
  /// </summary>
  /// <remarks>
  /// Derived classes must call base.Invalidate.
  /// </remarks>
  override x.OnInvalidated(e:InvalidateEventArgs) =  
    suppressOnInvalidated <- true    
    base.OnInvalidated(e)    
    if not suppressInvalidate then
      x.Invalidate(e.InvalidRect)
    suppressOnInvalidated <- false

  /// <summary>
  /// Processes Windows messages.
  /// Since VSLab uses a hidden form to proxy a Visual Studio Tool Window into F# Interactive
  /// this method should never be oveddiden by Viewlet subclass.
  /// </summary>
  /// <param name="m"> the WM_ Message to be processed</param>
  override x.WndProc(m) =
    let loword (p:IntPtr) = (int (int64 p)) &&& 0xffff
    let hiword (p:IntPtr) = (int (int64 p)) >>> 16

    let (msg:Messages) = enum (int m.Msg)
    match msg with
    | Messages.WM_MOUSEMOVE ->
      mousemoveleave <- true
      if not mousein then
        x.OnMouseEnter(EventArgs.Empty)
        mousein <- true
      base.WndProc(&m)
    | Messages.WM_MOUSELEAVE ->
      if not mousemoveleave then
        mousein <- false
        x.OnMouseLeave(EventArgs.Empty)
      mousemoveleave <- false
    | Messages.WM_LBUTTONDOWN
    | Messages.WM_RBUTTONDOWN
    | Messages.WM_MBUTTONDOWN ->
      clickstate <- ClickState.Down
      base.WndProc(&m)    
    | Messages.WM_LBUTTONDBLCLK
    | Messages.WM_RBUTTONDBLCLK
    | Messages.WM_MBUTTONDBLCLK ->
      clickstate <- ClickState.DoubleClick
      base.WndProc(&m)
    | Messages.WM_LBUTTONUP ->
      if x.GetStyle(ControlStyles.StandardClick) && clickstate = ClickState.Down then
        x.OnClick(new MouseEventArgs(MouseButtons.Left, 1, loword(m.LParam), hiword(m.LParam), 0))
        x.OnMouseClick(new MouseEventArgs(MouseButtons.Left, 1, loword(m.LParam), hiword(m.LParam), 0))
      if x.GetStyle(ControlStyles.StandardDoubleClick) && clickstate = ClickState.DoubleClick then
        x.OnDoubleClick(new MouseEventArgs(MouseButtons.Left, 1, loword(m.LParam), hiword(m.LParam), 0))
        x.OnMouseDoubleClick(new MouseEventArgs(MouseButtons.Left, 1, loword(m.LParam), hiword(m.LParam), 0))
      clickstate <- ClickState.None
      base.WndProc(&m)      
    | Messages.WM_RBUTTONUP -> 
      if x.GetStyle(ControlStyles.StandardClick) && clickstate = ClickState.Down then
        x.OnClick(new MouseEventArgs(MouseButtons.Right, 1, loword(m.LParam), hiword(m.LParam), 0))
        x.OnMouseClick(new MouseEventArgs(MouseButtons.Right, 1, loword(m.LParam), hiword(m.LParam), 0))
      if x.GetStyle(ControlStyles.StandardDoubleClick) && clickstate = ClickState.DoubleClick then
        x.OnDoubleClick(new MouseEventArgs(MouseButtons.Right, 1, loword(m.LParam), hiword(m.LParam), 0))
        x.OnMouseDoubleClick(new MouseEventArgs(MouseButtons.Right, 1, loword(m.LParam), hiword(m.LParam), 0))
      clickstate <- ClickState.None
      base.WndProc(&m)      
    | Messages.WM_MBUTTONUP ->
      if x.GetStyle(ControlStyles.StandardClick) && clickstate = ClickState.Down then
        x.OnClick(new MouseEventArgs(MouseButtons.Middle, 1, loword(m.LParam), hiword(m.LParam), 0))
        x.OnMouseClick(new MouseEventArgs(MouseButtons.Middle, 1, loword(m.LParam), hiword(m.LParam), 0))
      if x.GetStyle(ControlStyles.StandardDoubleClick) && clickstate = ClickState.DoubleClick then
        x.OnDoubleClick(new MouseEventArgs(MouseButtons.Middle, 1, loword(m.LParam), hiword(m.LParam), 0))
        x.OnMouseDoubleClick(new MouseEventArgs(MouseButtons.Middle, 1, loword(m.LParam), hiword(m.LParam), 0))
      clickstate <- ClickState.None
      base.WndProc(&m)
    | Messages.WM_APP_PAINT | Messages.WM_APP_INVALIDATE ->
      if target <> IntPtr.Zero then
        let decode a = ((a >>> 16) &&& 0xFFFF, a &&& 0xFFFF)
        let left, top = decode (m.WParam.ToInt32())
        let width, height = decode (m.LParam.ToInt32())
        let rect = new Rectangle(left, top, width, height)

        if doubleBuffer = null then
          use g = Graphics.FromHwnd(target)
          g.SetClip(new RectangleF(float32 left, float32 top, float32 width, float32 height))
          let pe = new PaintEventArgs(g, rect)
          x.OnPaintBackground(pe)
          x.internalPaint(pe)
        else
          use g = Graphics.FromHwnd(target)
          use bg = Graphics.FromImage(doubleBuffer)
          g.SetClip(new RectangleF(float32 left, float32 top, float32 width, float32 height))
          bg.SetClip(new RectangleF(float32 left, float32 top, float32 width, float32 height))
          let pe = new PaintEventArgs(bg, rect)
          x.OnPaintBackground(pe)
          x.internalPaint(pe)
          g.DrawImage(doubleBuffer, left, top, new Rectangle(left, top, width, height), GraphicsUnit.Pixel)
    | Messages.WM_APP_ERASEBKGND ->
      ()
      // This code has been commented since the AllPaintingInWmPaint ensures that paint gets called properly
      //if target <> IntPtr.Zero then
        // BAD HACK
        // I always get the full client area instead of the rect to be redrawn
        // so I just remove this call and wait for WM_PAINT
        //let decode a = ((a >>> 16) &&& 0xFFFF, a &&& 0xFFFF)
        //let left, top = decode (m.WParam.ToInt32())
        //let width, height = decode (m.LParam.ToInt32())
        //let rect = new Rectangle(left, top, width, height)        

        //use g = Graphics.FromHwnd(target)      
        //g.SetClip(new RectangleF(float32 left, float32 top, float32 width, float32 height))
        //let pe = new PaintEventArgs(g, rect)
        //x.OnPaintBackground(pe)
    | Messages.WM_SIZE ->
      base.WndProc(&m)
      width <- m.LParam.ToInt32() &&& 0xFFFF
      height <- (m.LParam.ToInt32() >>> 16) &&& 0xFFFF
      x.ClientSize <- new Size(width, height)
      if doubleBuffer <> null then
        doubleBuffer.Dispose()
        doubleBuffer <- new Bitmap(width, height)
      if target <> IntPtr.Zero then
        x.OnSizeChanged(new EventArgs())
    | Messages.WM_APP_VISIBLECHANGED ->
      if target <> IntPtr.Zero then
        let visible = m.WParam.ToInt32() <> 0
        x.Visible <- visible
        populateHiddenFun()
        x.OnViewletVisibilityChange(visible)
    | _ ->
      if m.Msg <> WM_MOUSEENTER then
        base.WndProc(&m)    

  /// <summary>
  /// This method is not supported by Viewlets
  /// </summary>
  override x.OnBindingContextChanged (e) =
    base.OnBindingContextChanged(e)
  
  /// <summary>
  /// This method is not supported by Viewlets
  /// </summary>
  override x.OnCausesValidationChanged (e) =
    failwith "OnCausesValidationChanged: Disallowed method for Viewlet"

  /// <summary>
  /// This method is not supported by Viewlets
  /// </summary>
  override x.OnChangeUICues (e) =
    failwith "OnChangeUICues: Disallowed method for Viewlet"

  /// <summary>
  /// This method is not supported by Viewlets
  /// </summary>
  override x.OnControlAdded (e) =
    failwith "OnControlAdded: Disallowed method for Viewlet"

  /// <summary>
  /// This method is not supported by Viewlets
  /// </summary>
  override x.OnControlRemoved (e) =
    failwith "OnControlRemoved: Disallowed method for Viewlet"

  /// <summary>
  /// This method is not supported by Viewlets
  /// </summary>
  override x.OnDockChanged (e) =
    failwith "OnDockChanged: Disallowed method for Viewlet"

  /// <summary>
  /// This method is not supported by Viewlets
  /// </summary>
  override x.OnHelpRequested (e) =
    failwith "OnHelpRequested: Disallowed method for Viewlet"

  /// <summary>
  /// This method is not supported by Viewlets
  /// </summary>
  override x.OnLayout (e) =
    base.OnLayout(e)

  /// <summary>
  /// This method is not supported by Viewlets
  /// </summary>
  override x.OnLocationChanged (e) =
    failwith "OnLocationChanged: Disallowed method for Viewlet"

  /// <summary>
  /// This method is not supported by Viewlets
  /// </summary>
  override x.OnMarginChanged (e) =
    failwith "OnMarginChanged: Disallowed method for Viewlet"

  /// <summary>
  /// This method is not supported by Viewlets
  /// </summary>
  override x.OnMove e =
    base.OnMove e

  /// <summary>
  /// This method is not supported by Viewlets
  /// </summary>
  override x.OnNotifyMessage (e) =
    failwith "OnNotifyMessage: Disallowed method for Viewlet"

  /// <summary>
  /// This method is not supported by Viewlets
  /// </summary>
  override x.OnPaddingChanged (e) =
    failwith "OnPaddingChanged: Disallowed method for Viewlet"

  /// <summary>
  /// This method is not supported by Viewlets
  /// </summary>
  override x.OnParentBackColorChanged (e) =
    failwith "OnParentBackColorChanged: Disallowed method for Viewlet"

  /// <summary>
  /// This method is not supported by Viewlets
  /// </summary>
  override x.OnParentBackgroundImageChanged (e) =
    failwith "OnParentBackgroundImageChanged: Disallowed method for Viewlet"

  /// <summary>
  /// This method is not supported by Viewlets
  /// </summary>
  override x.OnParentBindingContextChanged (e) =
    failwith "OnParentBindingContextChanged: Disallowed method for Viewlet"

  /// <summary>
  /// This method is not supported by Viewlets
  /// </summary>
  override x.OnParentChanged (e) =
    base.OnParentChanged(e)

  /// <summary>
  /// This method is not supported by Viewlets
  /// </summary>
  override x.OnParentCursorChanged (e) =
    failwith "OnParentCursorChanged: Disallowed method for Viewlet"

  /// <summary>
  /// This method is not supported by Viewlets
  /// </summary>
  override x.OnParentEnabledChanged (e) =
    failwith "OnParentEnabledChanged: Disallowed method for Viewlet"

  /// <summary>
  /// This method is not supported by Viewlets
  /// </summary>
  override x.OnParentFontChanged (e) =
    failwith "OnParentFontChanged: Disallowed method for Viewlet"

  /// <summary>
  /// This method is not supported by Viewlets
  /// </summary>
  override x.OnParentForeColorChanged (e) =
    failwith "OnParentForeColorChanged: Disallowed method for Viewlet"

  /// <summary>
  /// This method is not supported by Viewlets
  /// </summary>
  override x.OnParentRightToLeftChanged (e) =
    failwith "OnParentRightToLeftChanged: Disallowed method for Viewlet"

  /// <summary>
  /// This method is not supported by Viewlets
  /// </summary>
  override x.OnParentVisibleChanged (e) =
    failwith "OnParentVisibleChanged: Disallowed method for Viewlet"

  /// <summary>
  /// This method is not supported by Viewlets
  /// </summary>
  override x.OnPrint (e) =
    failwith "OnPrint: Disallowed method for Viewlet"

  /// <summary>
  /// This method is not supported by Viewlets
  /// </summary>
  override x.OnRegionChanged (e) =
    failwith "OnRegionChanged: Disallowed method for Viewlet"

  /// <summary>
  /// This method is not supported by Viewlets
  /// </summary>
  override x.OnStyleChanged (e) =
    failwith "OnStyleChanged: Disallowed method for Viewlet"

  /// <summary>
  /// This method is not supported by Viewlets
  /// </summary>
  override x.OnTabIndexChanged (e) =
    failwith "OnTabIndexChanged: Disallowed method for Viewlet"

  /// <summary>
  /// This method is not supported by Viewlets
  /// </summary>
  override x.OnTabStopChanged (e) =
    failwith "OnTabStopChanged: Disallowed method for Viewlet"

  /// <summary>
  /// This method is not supported by Viewlets
  /// </summary>
  override x.OnValidated (e) = () // FIXME: Check, sometimes is raised
    //failwith "OnValidated: Disallowed method for Viewlet"

  /// <summary>
  /// This method is not supported by Viewlets
  /// </summary>
  override x.OnValidating (e) = () // FIXME: Check, sometimes is raised
    //failwith "OnValidating: Disallowed method for Viewlet"

  /// <summary>
  /// The visibility state of the Viewlet is changed.
  /// </summary>
  abstract member OnViewletVisibilityChange : bool -> unit
  /// <summary>
  /// The visibility state of the Viewlet is changed.
  /// </summary>
  /// <param name="visible"> the new state</param>
  default x.OnViewletVisibilityChange (visible) = ()  

  /// <summary>
  /// The list of the categories present in the Viewlet Property Grid
  /// </summary>
  abstract member BrowsableCategories : unit -> string[]
  /// <summary>
  /// The list of the categories present in the Viewlet Property Grid
  /// </summary>
  default x.BrowsableCategories () = null

  /// <summary>
  /// Shows the Viewlet's Property Grid  
  /// </summary>
  member x.ShowProperty() =
    let f = new Form(Text=("Properties for "+name))
    let pg = new PropertyGrid()
    pg.SelectedObject <- x
    let ba = x.BrowsableCategories()
    if ba <> null then
      pg.BrowsableAttributes <- new ComponentModel.AttributeCollection(Array.map (fun (s:string) -> (new ComponentModel.CategoryAttribute(s) :> Attribute)) ba)
    pg.Dock <- DockStyle.Fill
    f.Controls.Add(pg)
    f.TopMost <- true
    f.Show()